package com.ideaaedi.commonspring.validation;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import javax.annotation.Nonnull;

/**
 * 增强LocalValidatorFactoryBean（主要增强{@link SpringValidatorAdapter}中的相关实现）， 若jsr303校验失败，则记录被校验对象的内容.
 * <hr >
 * spring-boot可以参考org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration#defaultValidator进行启用. 启用示例
 * <hr >
 * <pre>
 * {@code
 * import com.ideaaedi.commonspring.validation.LogLocalValidatorFactoryBean;
 * import jakarta.validation.Validator;
 * import org.springframework.beans.factory.ObjectProvider;
 * import org.springframework.beans.factory.config.BeanDefinition;
 * import org.springframework.boot.autoconfigure.AutoConfiguration;
 * import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 * import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 * import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
 * import org.springframework.boot.autoconfigure.validation.ValidationConfigurationCustomizer;
 * import org.springframework.boot.validation.MessageInterpolatorFactory;
 * import org.springframework.context.ApplicationContext;
 * import org.springframework.context.annotation.Bean;
 * import org.springframework.context.annotation.Configuration;
 * import org.springframework.context.annotation.Role;
 * import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 *
 * @Configuration
 * @AutoConfiguration
 * @AutoConfigureBefore(ValidationAutoConfiguration.class)
 * public class SpringValidatorAdapterConfig {
 *
 *     @Bean
 *     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 *     @ConditionalOnMissingBean(Validator.class)
 *     public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext,
 *                                                              ObjectProvider<ValidationConfigurationCustomizer> customizers) {
 *         LocalValidatorFactoryBean factoryBean = new LogLocalValidatorFactoryBean();
 *         factoryBean.setConfigurationInitializer((configuration) -> customizers.orderedStream()
 *                 .forEach((customizer) -> customizer.customize(configuration)));
 *         MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(applicationContext);
 *         factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
 *         return factoryBean;
 *     }
 *
 * }
 * }
 * </pre>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.6.0
 */
@Slf4j
public class LogLocalValidatorFactoryBean extends LocalValidatorFactoryBean {
    
    @Override
    public void validate(@Nonnull Object target,@Nonnull Errors errors) {
        super.validate(target, errors);
        doLog(target, errors);
    }
    
    @Override
    public void validate(@Nonnull Object target, @Nonnull Errors errors, @Nonnull Object... validationHints) {
        super.validate(target, errors, validationHints);
        doLog(target, errors);
    }
    
    @Override
    public void validateValue(@Nonnull Class<?> targetType, @Nonnull String fieldName, Object value, @Nonnull Errors errors,
                              @Nonnull Object... validationHints) {
        super.validateValue(targetType, fieldName, value, errors, validationHints);
        doLog(value, errors);
    }
    
    /**
     * 记录当前被校验对象
     */
    private static void doLog(Object target, Errors errors) {
            if (errors != null && !CollectionUtils.isEmpty(errors.getAllErrors())) {
                if (log.isInfoEnabled()) {
                    String str;
                    try {
                        str = JSON.toJSONString(target);
                        log.info("curr object -> {}", str);
                    } catch (Exception e) {
                        str = String.valueOf(target);
                        log.info("curr object -> {}. be careful that JSON.toJSONString(target) occur exception, e.getMessage() -> {}", str, e.getMessage());
                    }
                }
            }
    }
}
